#pragma once
#include <brpc/channel.h>
#include <mutex>
#include "logger.hpp"

// brpc的二次封装: 将etcd和brpc进行整合 rpc解决了如何服务的问题, etcd解决了能向谁发起服务的问题, 
// 这个封装的本质还是提供服务, 让提供服务功能更齐全, 更易于使用, 只不过你不用关心向谁发起服务了(别人提供给你什么, 你就向谁发起)
//合起来就是自动的提供了信道, 可以通过信道发起服务请求


//1. 封装单个服务的信道管理类  为什么有?
//  能让我们知道哪个服务有哪个Channel可以进行通信


namespace wufan_im{
class ServiceChannel{
    public:
        using ptr = std::shared_ptr<ServiceChannel>;
        using ChannelPtr = std::shared_ptr<brpc::Channel>;
        ServiceChannel(const std::string& name)
        :_service_name(name), _index(0)
        {}
        // 服务上线了一个节点, 则调用append新增信道
        void append(const std::string& host) {
            auto channel = std::make_shared<brpc::Channel>();
            brpc::ChannelOptions options;
            options.connect_timeout_ms = -1;    //连接等待超时时间, -1表示一直等待
            options.timeout_ms = -1;        //rpc请求等待超时时间, -1表示一直等待
            options.max_retry = 3;      //最大重试次数
            options.protocol = "baidu_std"; //序列化协议, 默认使用baidu_std
            int ret = channel->Init(host.c_str(), &options);//参数1: 服务器地址  参数2: 客户端连接时的配置
            if (ret == -1) {
                LOG_ERROR("初始化{}-{}信道失败!", _service_name, host);
                return;
            }
            std::unique_lock<std::mutex> lock(_mutex);
            _hosts.insert(std::make_pair(host, channel));
            _channels.push_back(channel);
        }
        // 服务下线了一个节点, 则调用remove释放信道
        void remove(const std::string& host) {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _hosts.find(host);
            if (it == _hosts.end()) {
                LOG_WARN("{}-{}节点删除信道时, 没有找到信道信息! ", _service_name, host);
                return;
            }
            for (auto vit = _channels.begin(); vit != _channels.end(); ++vit) {
                if (*vit == it->second) {
                    _channels.erase(vit);
                    break;
                }
            }
            _hosts.erase(it);
        }
        // 通过RR轮转策略, 获取一个Channel用于发起对应服务的Rpc调用
        ChannelPtr choose() {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_channels.size() == 0) {
                LOG_ERROR("当前没有能提供 {} 服务的节点! ", _service_name);
                return ChannelPtr();
            }
            int32_t idx = ++_index % _channels.size();
            return _channels[idx];
        }
    private:
        std::mutex _mutex;  //来保证_channels内部的操作安全     为什么会有线程问题? 因为一个服务对应多个服务主机, 多个主机同时append/remove就会产生线程问题
        int32_t _index;     //当前轮转下标计数器 - 我们采用RR轮转的方式
        std::string _service_name;  //服务名称
        std::vector<ChannelPtr> _channels;  //当前服务对应的信道集合 - 这里也可以直接定义
        std::unordered_map<std::string, ChannelPtr> _hosts;  //提供服务的主机地址与信道映射关系
};


// 总体的服务信道管理类
class ServiceManager {
    public:
        using ptr = std::shared_ptr<ServiceManager>;
        // 通过服务名称, 获取到一个信道, 有了信道, 就能给服务发起rpc请求了
        // 获取制定服务的节点通信
        ServiceChannel::ChannelPtr choose(const std::string& service_name) {
            std::unique_lock<std::mutex> lock(_mutex);
            auto sit = _services.find(service_name);
            if (sit == _services.end()) {
                LOG_ERROR("当前没有能提供 {} 服务的节点! ", service_name);
                return ServiceChannel::ChannelPtr();
            }
            return sit->second->choose();
        }
        // 声明: 我关注哪些服务的上下线, 不关心的就不需要管理了
        void declared(const std::string& service_name) {
            std::unique_lock<std::mutex> lock(_mutex);
            _follow_services.insert(service_name);
        }
        // 服务上线时调用的回调接口, 将服务节点管理起来     -- 提供给etcd的接口
        void onServiceOnline(const std::string& service_instance, const std::string& host) {
            std::string service_name = getServiceName(service_instance);
            ServiceChannel::ptr service;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto fit = _follow_services.find(service_name);
                if (fit == _follow_services.end()) {
                    LOG_DEBUG("{}-{} 服务上线了, 但是当前并不关心!", service_name, host);
                    return;
                }
                // 先获取管理对象, 没有则创建, 有则添加节点
                auto sit = _services.find(service_name);
                if (sit == _services.end()) {
                    service = std::make_shared<ServiceChannel>(service_name);
                    _services.insert(std::make_pair(service_name, service));
                } else {
                    service = sit->second;
                }
            }
            if (!service) {
                LOG_ERROR("新增 {} 服务管理节点失败! ", service_name);
                return;
            }
            service->append(host);
        }
        // 服务下线时调用的回调接口, 从服务信道管理中, 删除指定节点信道     -- 提供给etcd的接口
        void onServiceOffline(const std::string& service_instance, const std::string& host) {
            std::string service_name = getServiceName(service_instance);
            ServiceChannel::ptr service;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto fit = _follow_services.find(service_name);
                if (fit == _follow_services.end()) {
                    LOG_DEBUG("{}-{} 服务下线了, 但是当前并不关心! ", service_name, host);
                    return;
                }
                auto sit = _services.find(service_name);
                if (sit == _services.end()) {
                    LOG_WARN("删除{}服务节点时, 没有找到管理对象", service_name);
                    return;
                }
                service = sit->second;
            }
            service->remove(host);
        }
    private:
        //  我们管理服务的时候, 希望的是添加服务名, 而不是添加服务的实例名
        std::string getServiceName(const std::string& service_instance) {
            auto pos = service_instance.find_last_of('/');
            if (pos == std::string::npos) return service_instance;
            return service_instance.substr(0, pos);
        }
    private:
        std::mutex _mutex;
        std::unordered_set<std::string> _follow_services;
        std::unordered_map<std::string, ServiceChannel::ptr> _services;
};
}


// #pragma once
// #include <brpc/channel.h>
// #include <string>
// #include <vector>
// #include <unordered_map>
// #include <mutex>
// #include "logger.hpp"

// namespace wufan_im{
// //1. 封装单个服务的信道管理类:
// class ServiceChannel {
//     public:
//         using ptr = std::shared_ptr<ServiceChannel>;
//         using ChannelPtr = std::shared_ptr<brpc::Channel>;
//         ServiceChannel(const std::string &name):
//             _service_name(name), _index(0){}
//         //服务上线了一个节点，则调用append新增信道
//         void append(const std::string &host) {
//             auto channel = std::make_shared<brpc::Channel>();
//             brpc::ChannelOptions options;
//             options.connect_timeout_ms = -1;
//             options.timeout_ms = -1;
//             options.max_retry = 3;
//             options.protocol = "baidu_std";
//             int ret = channel->Init(host.c_str(), &options);
//             if (ret == -1) {
//                 LOG_ERROR("初始化{}-{}信道失败!", _service_name, host);
//                 return;
//             }
//             std::unique_lock<std::mutex> lock(_mutex);
//             _hosts.insert(std::make_pair(host, channel));
//             _channels.push_back(channel);
//         }
//         //服务下线了一个节点，则调用remove释放信道
//         void remove(const std::string &host) {
//             std::unique_lock<std::mutex> lock(_mutex);
//             auto it = _hosts.find(host);
//             if (it == _hosts.end()) {
//                 LOG_WARN("{}-{}节点删除信道时，没有找到信道信息！", _service_name, host);
//                 return;
//             }
//             for (auto vit = _channels.begin(); vit != _channels.end(); ++vit) {
//                 if (*vit == it->second) {
//                     _channels.erase(vit);
//                     break;
//                 }
//             }
//             _hosts.erase(it);
//         }
//         //通过RR轮转策略，获取一个Channel用于发起对应服务的Rpc调用
//         ChannelPtr choose() {
//             std::unique_lock<std::mutex> lock(_mutex);
//             if (_channels.size() == 0) {
//                 LOG_ERROR("当前没有能够提供 {} 服务的节点！", _service_name);
//                 return ChannelPtr();
//             }
//             int32_t idx = _index++ % _channels.size();
//             return _channels[idx];
//         }
//     private:
//         std::mutex _mutex;
//         int32_t _index; //当前轮转下标计数器
//         std::string _service_name;//服务名称
//         std::vector<ChannelPtr> _channels; //当前服务对应的信道集合
//         std::unordered_map<std::string, ChannelPtr> _hosts; //主机地址与信道映射关系
// };

// //总体的服务信道管理类
// class ServiceManager {
//     public:
//         using ptr = std::shared_ptr<ServiceManager>;
//         ServiceManager() {}
//         //获取指定服务的节点信道
//         ServiceChannel::ChannelPtr choose(const std::string &service_name) {
//             std::unique_lock<std::mutex> lock(_mutex);
//             auto sit = _services.find(service_name);
//             if (sit == _services.end()) {
//                 LOG_ERROR("当前没有能够提供 {} 服务的节点！", service_name);
//                 return ServiceChannel::ChannelPtr();
//             }
//             return sit->second->choose();
//         }
//         //先声明，我关注哪些服务的上下线，不关心的就不需要管理了
//         void declared(const std::string &service_name) {
//             std::unique_lock<std::mutex> lock(_mutex);
//             _follow_services.insert(service_name);
//         }
//         //服务上线时调用的回调接口，将服务节点管理起来
//         void onServiceOnline(const std::string &service_instance, const std::string &host) {
//             std::string service_name = getServiceName(service_instance);
//             ServiceChannel::ptr service;
//             {
//                 std::unique_lock<std::mutex> lock(_mutex);
//                 auto fit = _follow_services.find(service_name);
//                 if (fit == _follow_services.end()) {
//                     LOG_DEBUG("{}-{} 服务上线了，但是当前并不关心！", service_name, host);
//                     return;
//                 }
//                 //先获取管理对象，没有则创建，有则添加节点
//                 auto sit = _services.find(service_name);
//                 if (sit == _services.end()) {
//                     service = std::make_shared<ServiceChannel>(service_name);
//                     _services.insert(std::make_pair(service_name, service));
//                 }else {
//                     service = sit->second;
//                 }
//             }
//             if (!service) {
//                 LOG_ERROR("新增 {} 服务管理节点失败！", service_name);
//                 return ;
//             }
//             service->append(host);
//             LOG_DEBUG("{}-{} 服务上线新节点，进行添加管理！", service_name, host);
//         }
//         //服务下线时调用的回调接口，从服务信道管理中，删除指定节点信道
//         void onServiceOffline(const std::string &service_instance, const std::string &host) {
//             std::string service_name = getServiceName(service_instance);
//             ServiceChannel::ptr service;
//             {
//                 std::unique_lock<std::mutex> lock(_mutex);
//                 auto fit = _follow_services.find(service_name);
//                 if (fit == _follow_services.end()) {
//                     LOG_DEBUG("{}-{} 服务下线了，但是当前并不关心！", service_name, host);
//                     return;
//                 }
//                 //先获取管理对象，没有则创建，有则添加节点
//                 auto sit = _services.find(service_name);
//                 if (sit == _services.end()) {
//                     LOG_WARN("删除{}服务节点时，没有找到管理对象", service_name);
//                     return;
//                 }
//                 service = sit->second;
//             }
//             service->remove(host);
//             LOG_DEBUG("{}-{} 服务下线节点，进行删除管理！", service_name, host);
//         }
//     private:
//         std::string getServiceName(const std::string &service_instance) {
//             auto pos = service_instance.find_last_of('/');
//             if (pos == std::string::npos) return service_instance;
//             return service_instance.substr(0, pos);
//         }
//     private:
//         std::mutex _mutex;
//         std::unordered_set<std::string> _follow_services;
//         std::unordered_map<std::string, ServiceChannel::ptr> _services;
// };
// }